#include "../../../dxhead/dxegheader.h"
#include "../inc/xDxApp.h"

#pragma comment(lib,"Shell32.lib")

namespace Sky
{

LRESULT CALLBACK StaticWndProc2 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case 	WM_CREATE:
		//SetTimer (NULL, 0, 1000, dTimerProc) ;
		return 0 ;

	case 	WM_DESTROY:
		//KillTimer (hwnd, 0) ;
		PostQuitMessage (0) ;
		return 0 ;
	}
	return DefWindowProc (hwnd, message, wParam, lParam) ;
}
INT_PTR CALLBACK StaticWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	xDxApp* app = (xDxApp*)xGlobal<xDxApp*>::get();
	xState* state = app->getState();
	if(XE_CONSUMED == app->HandleWinPorc(hWnd,uMsg,wParam,lParam))
		return 0;
	switch (uMsg)
	{
	case WM_CLOSE:
		break;
	case WM_CREATE:
		break;
	case WM_COMMAND:
		break;
	case WM_SIZE:
		if(!state->m_bWindowed)break;
		//only handle the windowed message
		if(SIZE_RESTORED==wParam)
		{
			if(state->m_bMinimized==vTrue)
			{
				state->m_bMinimized=vFalse;
				app->AddPauseCount(-1,-1);
			}
		}
		else if(SIZE_MINIMIZED==wParam)
		{
			if(state->m_bMinimized==vFalse)
			{
				state->m_bMinimized=vTrue;
				app->AddPauseCount(1,1);
			}
		}
		return 0;
		break;
	case WM_ACTIVATEAPP:
		//this message is sent immediately after ShowWindow() was called,
		//m_bMinimizedWhileFullScreen means the state of before WM_ACTIVATEAPP
		//if(device->GetSettings()->PresentParams.Windowed)break;
		//only handle the full screen message
		if(vTrue == wParam)
		{
			if(state->m_bMinimizedWhileFullScreen == vTrue)
			{
				state->m_bMinimizedWhileFullScreen = vFalse;
				app->AddPauseCount(-1,-1);
			}
		}
		else
		{
			if(state->m_bMinimizedWhileFullScreen == vFalse)
			{
				state->m_bMinimizedWhileFullScreen = vTrue;
				app->AddPauseCount(1,1);
			}
		}
		return 0;
		break;
	case WM_ENTERSIZEMOVE:
		app->AddPauseCount(1,1);
		return 0;
		break;
	case WM_EXITSIZEMOVE:
		app->AddPauseCount(-1,-1);
		return 0;
		break;
	case WM_ENTERMENULOOP:
		app->AddPauseCount(1,1);
		return 0;
		break;
	case WM_EXITMENULOOP:
		app->AddPauseCount(-1,-1);
		return 0;
		break;
	case WM_PAINT:
		app->Render3DObjects();
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
		break;
	
	}
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
xDxApp::xDxApp(TVoid)
{
	xGlobal<xDxApp*>::set(this); 
	InitState();
}
xDxApp::~xDxApp(TVoid)
{
	
}
TResult xDxApp::Create3DEnv()
{
	if(m_state.m_bEnvCreated)return XE_OK;
	if(m_state.m_bCreateEnvCalled)
	{
		return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DEnv() has been failed"));
	}
	m_state.m_bCreateEnvCalled=vTrue;
	//other code
	myDeV_Ret_M(m_d3denv.onCreate(this))
	m_state.m_bEnvCreated=vTrue; 
	xInfor0A("env created...");
	return XE_OK;
}
TResult xDxApp::Create3DWindow(const TChar* strWindowTitle, TUint dwWinStyle,TInt nDefaultX,TInt nDefaultY,TInt nDefaultWidth,TInt nDefaultHeight, WNDPROC winProc)
{
	TResult hr=XE_OK;
	if(m_state.m_bWindowCreated)return XE_OK;
	if(m_state.m_bCreateWindowCalled)
	{
		return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DWindow() has been failed"));
	}
	/*if(!m_state.m_bEnvCreated)
	{
		if(m_state.m_bCreateEnvCalled)
		{
			return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DEnv() has been failed"));
		}
		hr=Create3DEnv();
		if(FAILED(hr))
		{
			return hr;
		}
	}*/
	m_state.m_bCreateWindowCalled=vTrue;

	m_hInstance = (HINSTANCE)GetModuleHandle(vNull);
	TChar szExePath[MAX_PATH];
	GetModuleFileName(vNull,szExePath,MAX_PATH);

	WNDCLASSEX wndClass;
	m_AppName = skynew(String(String::dynamicString(_XT("D3DEWindowClass"))));
	wndClass.cbSize=sizeof(WNDCLASSEX);
	wndClass.style=CS_DBLCLKS;
	if(winProc==vNull)
		wndClass.lpfnWndProc=(WNDPROC)StaticWndProc;
	else
		wndClass.lpfnWndProc=winProc;
	wndClass.cbClsExtra=sizeof(xDxApp*);
	wndClass.cbWndExtra=0;
	wndClass.hInstance=m_hInstance;
	wndClass.hIcon = ExtractIcon(m_hInstance,szExePath,0);;
	wndClass.hCursor=LoadCursor(vNull,IDC_ARROW);
	wndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
	wndClass.lpszMenuName=vNull;
	wndClass.lpszClassName=m_AppName->toCharString();
	wndClass.hIconSm=vNull;
	
	if(!RegisterClassEx(&wndClass))
	{
		TUint dwError=GetLastError();
		if(dwError!=ERROR_CLASS_ALREADY_EXISTS)
		{
			return myWin_Trace_hr0(S,HRESULT_FROM_WIN32(dwError),_XT("RegisterClassEx() failed"));
		}
	}
	
	m_window.setTitle(skynew (String(String::dynamicString(strWindowTitle)))).setStyle(dwWinStyle).setStartX(nDefaultX).setStartY(nDefaultY).setWidth(nDefaultWidth).setHeight(nDefaultHeight);
	myDeV_Ret_M(m_window.onCreate(this));
	m_state.m_bWindowCreated=vTrue;
	m_window.show(vTrue);
	xInfor0A("window created...");
	return XE_OK;
}
TResult xDxApp::Set3DWindow(HWND hWnd,TBool bhandlemsg)
{
	TResult hr=XE_OK;
//	if(m_state.m_bWindowCreated)return XE_OK;
//	if(m_state.m_bCreateWindowCalled)
//	{
//		return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DWindow() has been failed"));
//	}
//	if(hWnd == vNull){
//		return myDe_Trace_hr0(I,XE_ARGUMENT,_XT("Set3DWindow() hWnd == vNull"));
//	}
//	if(bhandlemsg)
//	{
//		        // Switch window procedures
//#ifdef _WIN64
//        //LONG_PTR nResult = SetWindowLongPtr( hWnd, GWLP_WNDPROC, (LONG_PTR)StaticWndProc );
//#else
//       // LONG_PTR nResult = SetWindowLongPtr( hWnd, GWLP_WNDPROC, (LONG)(LONG_PTR)StaticWndProc );
//#endif 
//		 //TUint dwError = GetLastError();
//        //if( nResult == 0 )
//			//return DX_ERR( L"SetWindowLongPtr", HRESULT_FROM_WIN32(dwError) );
//	}
	if(!m_state.m_bEnvCreated)
	{
		if(m_state.m_bCreateEnvCalled)
		{
			return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DEnv() has failed"));
		}
		hr=Create3DEnv();
		if(FAILED(hr))
		{
			return hr;
		}
	}

	m_window.attach(hWnd);
	//m_state.m_bWindowCreated=vTrue;
	return XE_OK;
}
TResult xDxApp::Create3DDevice(UINT AdapterOrdinal, TBool bWindowed, 
								TInt nSuggestedBufferWidth, TInt nSuggestedBufferHeight)
{
	TResult hr=XE_OK;
	if(m_state.m_bDeviceCreated)return XE_OK;
	if(m_state.m_bCreateDeviceCalled)
	{
		return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DDevice() has been failed"));
	}
	if(!m_state.m_bEnvCreated)
	{
		if(m_state.m_bCreateEnvCalled)
		{
			return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DEnv() has been failed"));
		}
		hr=Create3DEnv();
		if(FAILED(hr))
		{
			return hr;
		}
	}
	if(!m_state.m_bWindowCreated)
	{
		if(m_state.m_bCreateWindowCalled)
		{
			return myDe_Trace_hr0(I,XE_ALREADYERROR,_XT("Create3DWindow() has been failed"));
		}
		hr=Create3DWindow();
		if(FAILED(hr))
		{
			return hr;
		}
	}
	
	m_state.m_bCreateDeviceCalled=vTrue;
	
	m_device.setAdapterOrdinal(AdapterOrdinal);

	m_device.setWindowed(bWindowed);
	//set the bellow state for the needs of changing fullscreen <--> windowed
	if(bWindowed==vTrue)
	{
		m_state.m_bMinimizedWhileFullScreen=vFalse;
	}
	else
	{
		m_state.m_bMinimized=vFalse;
	}
	if(nSuggestedBufferHeight<0||nSuggestedBufferWidth<0)
	{
		m_device.setBackBufferWidth(m_window.getWidth());
		m_device.setBackBufferHeight(m_window.getHeight());
	}
	else
	{
		m_device.setBackBufferWidth(nSuggestedBufferWidth);
		m_device.setBackBufferHeight(nSuggestedBufferHeight);
	}
	Create3DObjects();
	m_state.m_bDeviceCreated=vTrue;
	xInfor0A("device created...");
	return XE_OK;
}
TResult xDxApp::MainLoop(TBool bWinProc,TBool need3D)
{
	TResult hr;
	if(m_state.m_ExitCode!=0)
		return m_state.m_ExitCode;
	if(need3D)
	{
		if(!m_state.m_bDeviceCreated)
		{
			if(m_state.m_bCreateDeviceCalled)
			{
				return myDe_Trace_hr0(S,XE_ALREADYERROR,_XT("Create3DDevice() has been failed"));
			}
			hr=Create3DDevice();
			if(FAILED(hr))
			{
				return hr;
			}
		}
		if(!m_state.m_bEnvCreated||!m_state.m_bWindowCreated||!m_state.m_bDeviceCreated)
		{
			return myDe_Trace_hr0(S,XE_ALREADYERROR,_XT("!m_state.m_bEnvCreated||!m_state.m_bWindowCreated||!m_state.m_bDeviceCreated"));
		}
	}
	
	MSG msg;
	if(bWinProc)
	{
		while(m_state.m_ExitCode == ExitCode_Success)
		{
			if(vTrue == ::GetMessage(&msg,vNull,0U,0U))
			{
				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}
		}
	}
	else
	{
		while(m_state.m_ExitCode == ExitCode_Success)
		{
			if(vTrue == ::PeekMessage(&msg,vNull,0U,0U,PM_REMOVE))
			{
				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}
			HandleMessageLoop();
			Render3DObjects();
		}
	}
	return m_state.m_ExitCode;
}
TVoid	xDxApp::InitState()
{
	ZeroMemory(&m_state,sizeof(xState));
	m_hInstance=vNull;
	m_state.m_ShowMsgOnError=vTrue;
	m_state.m_ExitCode = ExitCode_Success;
	m_state.m_hr	= XE_OK;
	m_state.m_bActive=vTrue;

	//m_state.m_FuncIsDeviceAcceptable=vNull;
	//m_state.m_FuncModifyDeviceSettings=vNull;
	m_state.m_ContextIsDeviceAcceptable=vNull;
	m_state.m_ContextModifyDeviceSettings=vNull;
	m_state.m_ShowMsgOnError=vTrue;
	m_state.m_bMinimizedWhileFullScreen=vTrue;	//because before app starts,i assume it to be minized,and when it starts, it get the message as bellows and this will change
	m_state.m_bMinimized=vTrue;					//because before app starts,i assume it to be minized,and when it starts, it get the message as bellows and this will change
	m_state.m_RenderPauseCount=1;	//because when app starts,it will receive a WM_SIZE.SIZE_RESTORED and WM_ACTIVATEAPP.vTrue messages,so set it to 1
	m_state.m_TimePauseCount=1;		//because when app starts,it will receive a WM_SIZE.SIZE_RESTORED and WM_ACTIVATEAPP.vTrue messages,so set it to 1
}
TVoid	xDxApp::Shutdown(ExitCode code)
{
	
	m_state.m_ExitCode = code;
	//Delete3DObjects();
}
TResult	xDxApp::Create3DObjects()
{
	TResult hr=XE_OK;
	myDeV_Ret_M(onChangeSettings(&m_device));
	if(m_device.getDevice9() != vNull)
	{
		return myDe_Trace_hr0(S,XE_EXISTED,_XT("Device not released"));
	}
	hr = m_device.onCreate(this);
	if(hr == XE_RESLOST)
	{
		m_state.m_bDeviceLost=vTrue;
		return XE_OK;
	}
	else if(FAILED(hr))
	{
		myDx_Trace_hr0(S,hr,_XT("Device cann't created"));
		return XE_APICALL;
	}
	if(m_state.m_b3DObjectCreated==vFalse)
	{
		hr = onDeviceCreate(&m_device);
		if(FAILED(hr))
		{
			return (hr==XE_NOTFOUND) ? XE_NOTFOUND : hr;
		}
		m_state.m_b3DObjectCreated=vTrue;
	}
	if(m_state.m_b3DObjectReseted==vFalse)
	{
		hr=onDeviceReset(&m_device);
		if(FAILED(hr))
		{
			return (hr==XE_NOTFOUND) ? XE_NOTFOUND : hr;
		}
		m_state.m_b3DObjectReseted=vTrue;
	}
	return hr;
}
TResult	xDxApp::Reset3DObjects()
{
	TResult hr=XE_OK;
	if(m_state.m_b3DObjectReseted==vTrue)
	{
		onDeviceLost();
		m_state.m_b3DObjectReseted=vFalse;
	}
	hr = m_device.Reset();
	if(FAILED(hr))
	{
		if(hr==D3DERR_DEVICELOST)return XE_LOST;
		else 
			return myDx_Trace_hr0(S,hr,_XT("pd3dDevice->Reset()"));
	}
	if(m_state.m_b3DObjectReseted==vFalse)
	{
		hr = onDeviceReset(&m_device);
		if(FAILED(hr))
		{
			onDeviceLost();
			return hr;
		}
		m_state.m_b3DObjectReseted=vTrue;
	}
	return XE_OK;
}
TVoid	xDxApp::Render3DObjects()
{
	TResult hr=XE_OK;
	if(m_state.m_bDeviceLost||m_state.m_RenderPauseCount>0)
	{
		Sleep(50);
		if(m_state.m_RenderPauseCount<=0)
		{
			if(FAILED(m_device.getDevice9()->TestCooperativeLevel()))
			{
				if(FAILED(hr=Reset3DObjects()))
				{

					if(hr == XE_LOST)
					{
						SetExitCode(ExitCode_ComLost);
						SetResult(hr);
						return;
					}
					else if(hr == XE_SOMEFAILED)
					{
						SetExitCode(ExitCode_Failed);
						SetResult(hr);
						return;
					}
					else
					{
						SetExitCode(ExitCode_Failed);
						SetResult(hr);
					}
				}
			}
			m_state.m_bDeviceLost=vFalse;
		}
	}
	if(m_state.m_RenderPauseCount<=0)
	{
		hr = RenderSceneLoop();
		if(FAILED(hr))
		{
			if(XE_LOST == hr)
			{
				m_state.m_bDeviceLost=vTrue;
			}
			else 
			{
				SetExitCode(ExitCode_Failed);
				SetResult(hr);
			}
		}
	}
}
TResult	xDxApp::Delete3DObjects()
{
	if(m_state.m_b3DObjectReseted==vTrue)
	{
		onDeviceLost();
	}
	if(m_state.m_b3DObjectCreated==vTrue)
	{
		onDeviceDestroy();
	}
	if(m_device.Release() > 0)
	{
		return myDe_Trace_hr0(W,XE_NOTRELEASE,_XT("m_device.Release() > 0"));
	}
	m_state.m_bDeviceCreated=vFalse;
	return XE_OK;
}

TVoid	xDxApp::AddPauseCount(TInt PauseTimeCount,TInt PauseRenderCount)
{
	m_state.m_TimePauseCount	+= PauseTimeCount;
	m_state.m_RenderPauseCount	+= PauseRenderCount;
	if(m_state.m_RenderPauseCount > 0)
	{
		m_state.m_bActive=vFalse;
	}
	else
	{
		m_state.m_bActive=vTrue;
	}
}

TResult xDxApp::onChangeSettings( xDevice* device ) 
{
	return XE_OK;
}

ExitCode xDxApp::Exit()
{
	Delete3DObjects();
	return m_state.m_ExitCode;
}

DXAPI xDevice* xDxApp::getDevice()
{
	return &m_device;
}

DXAPI xState* xDxApp::getState()
{
	return &m_state;
}

DXAPI xD3DEnv* xDxApp::getD3DEnv()
{
	return &m_d3denv;
}

DXAPI xWindow* xDxApp::getWindow()
{
	return &m_window;
}
}